package com.gitee.sop.layui.adminserver.module.background.system.api.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.sop.layui.adminserver.module.background.system.api.service.param.*;
import com.gitee.sop.layui.adminserver.module.background.system.api.service.result.ServiceInfoVo;
import com.gitee.sop.layui.adminserver.module.background.system.api.service.result.ServiceInstanceVO;
import com.gitee.sop.layui.adminserver.module.background.system.bean.*;
import com.gitee.sop.layui.adminserver.module.background.system.common.ChannelOperation;
import com.gitee.sop.layui.adminserver.module.background.system.common.StatusEnum;
import com.gitee.sop.layui.adminserver.module.background.system.entity.ConfigGray;
import com.gitee.sop.layui.adminserver.module.background.system.entity.ConfigGrayInstance;
import com.gitee.sop.layui.adminserver.module.background.system.mapper.ConfigGrayInstanceMapper;
import com.gitee.sop.layui.adminserver.module.background.system.mapper.ConfigGrayMapper;
import com.gitee.sop.layui.adminserver.module.background.system.mapper.ConfigServiceRouteMapper;
import com.gitee.sop.layui.adminserver.module.background.system.service.ConfigPushService;
import com.gitee.sop.layui.adminserver.module.background.system.service.RegistryService;
import com.gitee.sop.layui.adminserver.module.background.system.service.ServerService;
import com.gitee.sop.layui.adminserver.module.base.constants.Result;
import com.gitee.sop.layui.adminserver.module.base.error.ServiceInterruptionException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author admin
 */
@RestController
@ApiDoc("服务管理-服务列表")
@Slf4j
public class ServiceApi {


    @Autowired
    private RegistryService registryService;

    @Autowired
    private ServerService serverService;

    @Autowired
    private ConfigGrayMapper configGrayMapper;

    @Autowired
    private ConfigGrayInstanceMapper configGrayInstanceMapper;

    @Autowired
    private ConfigServiceRouteMapper configServiceRouteMapper;

    @Autowired
    private ConfigPushService configPushService;

    @GetMapping("/api/registry.service.lists")
    @ApiDocMethod(description = "路由配置中的服务列表", elementClass = String.class)
    Result listServiceInfo(ServiceSearchParam param) {
        List<String> allServiceId = configServiceRouteMapper.listAllServiceId();
        List<String> collect = allServiceId
                .stream()
                .filter(serviceId -> {
                    if (StringUtils.isBlank(param.getServiceId())) {
                        return true;
                    } else {
                        return serviceId.contains(param.getServiceId());
                    }
                })
                .sorted()
                .collect(Collectors.toList());
        return Result.success(collect);
    }

    @Api(name = "service.custom.add")
    @ApiDocMethod(description = "添加服务")
    void addService(ServiceAddParam param) {
        throw ServiceInterruptionException.fail(500, "该功能已下线");
    }

    @Api(name = "service.custom.del")
    @ApiDocMethod(description = "删除自定义服务")
    void delService(ServiceSearchParam param) {
        throw ServiceInterruptionException.fail(500, "该功能已下线");
    }

    @GetMapping("/api/service.instance.lists")
    @ApiDocMethod(description = "获取注册中心的服务列表", elementClass = ServiceInfoVo.class)
    Result listService(ServiceSearchParam param) {
        List<ServiceInstanceVO> list = serverService.listService(param);
        return Result.success(list);
    }

    @Api(name = "service.instance.offline")
    @ApiDocMethod(description = "服务禁用")
    void serviceOffline(ServiceInstanceParam param) {
        try {
            registryService.offlineInstance(param.buildServiceInstance());
        } catch (Exception e) {
            log.error("服务禁用失败，param:{}", param, e);
            throw ServiceInterruptionException.fail(500, "服务禁用失败，请查看日志");
        }
    }

    @Api(name = "service.instance.online")
    @ApiDocMethod(description = "服务启用")
    void serviceOnline(ServiceInstanceParam param) throws IOException {
        try {
            registryService.onlineInstance(param.buildServiceInstance());
        } catch (Exception e) {
            log.error("服务启用失败，param:{}", param, e);
            throw ServiceInterruptionException.fail(500, "服务启用失败，请查看日志");
        }
    }

    @Api(name = "service.instance.env.pre.open")
    @ApiDocMethod(description = "预发布")
    void serviceEnvPre(ServiceInstanceParam param) throws IOException {
        try {
            MetadataEnum envPre = MetadataEnum.ENV_PRE;
            registryService.setMetadata(param.buildServiceInstance(), envPre.getKey(), envPre.getValue());
        } catch (Exception e) {
            log.error("预发布失败，param:{}", param, e);
            ServiceInterruptionException.fail(500, "预发布失败，请查看日志");
        }
    }

    @Api(name = "service.gray.config.get")
    @ApiDocMethod(description = "灰度配置--获取")
    ConfigGray serviceEnvGrayConfigGet(ServiceIdParam param) throws IOException {
        return this.getConfigGray(param.getServiceId());
    }

    @Api(name = "service.gray.config.save")
    @ApiDocMethod(description = "灰度配置--保存")
    void serviceEnvGrayConfigSave(ServiceGrayConfigParam param) throws IOException {
        String serviceId = param.getServiceId().toLowerCase();
        LambdaQueryWrapper<ConfigGray> wrapper = new QueryWrapper<ConfigGray>().lambda().eq(ConfigGray::getServiceId, serviceId);
        ConfigGray configGray = configGrayMapper.selectOne(wrapper);
        if (configGray == null) {
            configGray = new ConfigGray();
            configGray.setServiceId(serviceId);
            configGray.setNameVersionContent(param.getNameVersionContent());
            configGray.setUserKeyContent(param.getUserKeyContent());
            configGrayMapper.insert(configGray);
        } else {
            configGray.setNameVersionContent(param.getNameVersionContent());
            configGray.setUserKeyContent(param.getUserKeyContent());
            configGrayMapper.updateById(configGray);
        }
        this.sendServiceGrayMsg(serviceId, ChannelOperation.GRAY_USER_KEY_SET);
    }

    @Api(name = "service.instance.env.gray.open")
    @ApiDocMethod(description = "开启灰度发布")
    void serviceEnvGray(ServiceInstanceParam param) throws IOException {
        String serviceId = param.getServiceId().toLowerCase();
        ConfigGray configGray = this.getConfigGray(serviceId);
        if (configGray == null) {
            throw ServiceInterruptionException.fail(500, "请先设置灰度参数");
        }
        try {
            MetadataEnum envPre = MetadataEnum.ENV_GRAY;
            registryService.setMetadata(param.buildServiceInstance(), envPre.getKey(), envPre.getValue());
            String instanceId = param.getInstanceId();
            LambdaQueryWrapper<ConfigGrayInstance> wrapper = new QueryWrapper<ConfigGrayInstance>().lambda().eq(ConfigGrayInstance::getInstanceId, instanceId);
            ConfigGrayInstance configGrayInstance = configGrayInstanceMapper.selectOne(wrapper);
            if (configGrayInstance == null) {
                configGrayInstance = new ConfigGrayInstance();
                configGrayInstance.setServiceId(serviceId);
                configGrayInstance.setInstanceId(instanceId);
                configGrayInstance.setStatus(StatusEnum.STATUS_ENABLE.getStatus());
                configGrayInstanceMapper.insert(configGrayInstance);
            } else {
                configGrayInstance.setStatus(StatusEnum.STATUS_ENABLE.getStatus());
                configGrayInstance.setServiceId(serviceId);
                configGrayInstanceMapper.updateById(configGrayInstance);
            }
            this.sendServiceGrayMsg(instanceId, serviceId, ChannelOperation.GRAY_USER_KEY_OPEN);
        } catch (Exception e) {
            log.error("灰度发布失败，param:{}", param, e);
            throw ServiceInterruptionException.fail(500, "灰度发布失败，请查看日志");
        }
    }

    @Api(name = "service.instance.env.online")
    @ApiDocMethod(description = "上线")
    void serviceEnvOnline(ServiceInstance param) throws IOException {
        try {
            MetadataEnum envPre = MetadataEnum.ENV_ONLINE;
            registryService.setMetadata(param, envPre.getKey(), envPre.getValue());
            LambdaQueryWrapper<ConfigGrayInstance> wrapper = new QueryWrapper<ConfigGrayInstance>().lambda().eq(ConfigGrayInstance::getInstanceId, param.getInstanceId());
            ConfigGrayInstance configGrayInstance = configGrayInstanceMapper.selectOne(wrapper);
            if (configGrayInstance != null) {
                configGrayInstance.setStatus(StatusEnum.STATUS_DISABLE.getStatus());
                configGrayInstanceMapper.updateById(configGrayInstance);
                this.sendServiceGrayMsg(param.getInstanceId(), param.getServiceId().toLowerCase(), ChannelOperation.GRAY_USER_KEY_CLOSE);
            }
        } catch (Exception e) {
            log.error("上线失败，param:{}", param, e);
            throw ServiceInterruptionException.fail(500, "上线失败，请查看日志");
        }
    }

    private void sendServiceGrayMsg(String serviceId, ChannelOperation channelOperation) {
        this.sendServiceGrayMsg(null, serviceId, channelOperation);
    }

    private void sendServiceGrayMsg(String instanceId, String serviceId, ChannelOperation channelOperation) {
        ServiceGrayDefinition serviceGrayDefinition = new ServiceGrayDefinition();
        serviceGrayDefinition.setInstanceId(instanceId);
        serviceGrayDefinition.setServiceId(serviceId);
        ChannelMsg channelMsg = new ChannelMsg(channelOperation, serviceGrayDefinition);
        configPushService.publishConfig(NacosConfigs.DATA_ID_GRAY, NacosConfigs.GROUP_CHANNEL, channelMsg);
    }

    private ConfigGray getConfigGray(String serviceId) {
        LambdaQueryWrapper<ConfigGray> wrapper = new QueryWrapper<ConfigGray>().lambda().eq(ConfigGray::getServiceId, serviceId);
        return configGrayMapper.selectOne(wrapper);
    }

}
